﻿// -------------------------------------------------------------------------------------------------------------------------------
// <copyright file="TestAddIndexedDenseSymmetricMatrixFactor.cs" company="Exul">
//     Copyright (c) Exul. All rights reserved.
// </copyright>
// <author>
//     Exul
// </author>
// -------------------------------------------------------------------------------------------------------------------------------

namespace ExulLibrary.Mathematics.LinearAlgebra.Sequential.Tests.DenseSymmetricMatrixTest.DoubleExtensions
{
    #region Usings

    using System;
    using System.Globalization;

    using ExulLibrary.Exceptions;
    using ExulLibrary.Mathematics.LinearAlgebra.Sequential.DoubleExtensions;

    using NUnit.Framework;

    using Extensions = ExulLibrary.Mathematics.LinearAlgebra.Sequential.DoubleExtensions.DenseSymmetricMatrixExtensions;

    #endregion

    /// <summary>
    ///     Test suite for
    ///     <see cref="Extensions.Add(DenseSymmetricMatrix{double},int[],int[],DenseSymmetricMatrix{double},double)"/>.
    /// </summary>
    [TestFixture]
    [Category("ExulLibrary.Mathematics.LinearAlgebra.Sequential.DoubleExtensions.DenseSymmetricMatrixExtensions")]
    public sealed class TestAddIndexedDenseSymmetricMatrixFactor
    {
        /// <summary>
        ///     Tests the method when indexes of augend elements are non-negative and less than
        ///     size of dimensions of augend matrix.
        /// </summary>
        [Test]
        public void WhenAugendIndexesAreValid()
        {
            // Arrange
            const double Factor = 5;
            var augendData = new double[] { 10, 20, 30, 40, 50, 60 };
            var augend = new DenseSymmetricMatrix<double>(augendData);
            var addendData = new double[] { 1, 2, 3 };
            var addend = new DenseSymmetricMatrix<double>(addendData);
            var augendRowsIndexes = new[] { 0, 2 };
            var augendColumnsIndexes = new[] { 0, 1 };
            var expectedResult = new double[] { 15, 20, 30, 50, 65, 60 };

            // Act
            augend.Add(augendRowsIndexes, augendColumnsIndexes, addend, Factor);

            // Assert
            Assert.That(augend.Values, Is.EquivalentTo(expectedResult));
        }

        /// <summary>
        ///     Tests the method when collection of rows indexes of augend elements is empty.
        /// </summary>
        [Test]
        public void WhenAugendRowsIndexesCollectionIsEmpty()
        {
            // Arrange
            const double Factor = 5;
            var augendData = new double[] { 10, 20, 30, 40, 50, 60 };
            var augend = new DenseSymmetricMatrix<double>(augendData);
            var addendData = new double[] { 1, 2, 3 };
            var addend = new DenseSymmetricMatrix<double>(addendData);
            var augendRowsIndexes = new int[0];
            var augendColumnsIndexes = new[] { 0, 1 };
            var expectedResult = new double[] { 10, 20, 30, 40, 50, 60 };

            // Act
            augend.Add(augendRowsIndexes, augendColumnsIndexes, addend, Factor);

            // Assert
            Assert.That(augend.Values, Is.EquivalentTo(expectedResult));
        }

        /// <summary>
        ///     Tests the method when collection of columns indexes of augend elements is empty.
        /// </summary>
        [Test]
        public void WhenAugendColumnsIndexesCollectionIsEmpty()
        {
            // Arrange
            const double Factor = 5;
            var augendData = new double[] { 10, 20, 30, 40, 50, 60 };
            var augend = new DenseSymmetricMatrix<double>(augendData);
            var addendData = new double[] { 1, 2, 3 };
            var addend = new DenseSymmetricMatrix<double>(addendData);
            var augendRowsIndexes = new[] { 0, 2 };
            var augendColumnsIndexes = new int[0];
            var expectedResult = new double[] { 10, 20, 30, 40, 50, 60 };

            // Act
            augend.Add(augendRowsIndexes, augendColumnsIndexes, addend, Factor);

            // Assert
            Assert.That(augend.Values, Is.EquivalentTo(expectedResult));
        }

#if SAFE

        /// <summary>
        ///     Tests the method when augend is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenTargetIsNull()
        {
            // Arrange
            const double Factor = 5;
            var addendData = new double[] { 1, 2, 3 };
            var addend = new DenseSymmetricMatrix<double>(addendData);
            var augendRowsIndexes = new[] { 0, 2 };
            var augendColumnsIndexes = new[] { 0, 1 };

            // Act and Assert
            Assert.That(
                () => DenseSymmetricMatrixExtensions.Add(null, augendRowsIndexes, augendColumnsIndexes, addend, Factor),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("augend"));
        }

        /// <summary>
        ///     Tests the method when augend rows indexes collection is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenAugendRowsIndexesIsNull()
        {
            // Arrange
            const double Factor = 5;
            var augendData = new double[] { 10, 20, 30, 40, 50, 60 };
            var augend = new DenseSymmetricMatrix<double>(augendData);
            var addendData = new double[] { 1, 2, 3 };
            var addend = new DenseSymmetricMatrix<double>(addendData);
            var augendColumnsIndexes = new[] { 0, 1 };

            // Act and Assert
            Assert.That(
                () => augend.Add(null, augendColumnsIndexes, addend, Factor),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("augendRowsIndexes"));
        }

        /// <summary>
        ///     Tests the method when augend columns indexes collection is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenAugendColumnsIndexesIsNull()
        {
            // Arrange
            const double Factor = 5;
            var augendData = new double[] { 10, 20, 30, 40, 50, 60 };
            var augend = new DenseSymmetricMatrix<double>(augendData);
            var addendData = new double[] { 1, 2, 3 };
            var addend = new DenseSymmetricMatrix<double>(addendData);
            var augendRowsIndexes = new[] { 0, 2 };

            // Act and Assert
            Assert.That(
                () => augend.Add(augendRowsIndexes, null, addend, Factor),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("augendColumnsIndexes"));
        }

        /// <summary>
        ///     Tests the method when addend is <see langword="null"/>.
        /// </summary>
        [Test]
        public void WhenAddendIsNull()
        {
            // Arrange
            const double Factor = 5;
            var augendData = new double[] { 10, 20, 30, 40, 50, 60 };
            var augend = new DenseSymmetricMatrix<double>(augendData);
            var augendRowsIndexes = new[] { 0, 2 };
            var augendColumnsIndexes = new[] { 0, 1 };

            // Act and Assert
            Assert.That(
                () => augend.Add(augendRowsIndexes, augendColumnsIndexes, null, Factor),
                Throws.TypeOf<ArgumentNullException>().With.Property("ParamName").EqualTo("addend"));
        }

        /// <summary>
        ///     Tests the method when an augend row index is negative or greater than or equal to size of augend matrix.
        /// </summary>
        /// <param name="position">
        ///     The position of index.
        /// </param>
        /// <param name="augendRowIndex">
        ///     The row index of augend element.
        /// </param>
        [Test]
        public void WhenAnAugendRowIndexIsOutOfRange([Values(0, 1)] int position, [Values(-1, 3)] int augendRowIndex)
        {
            // Arrange
            const double Factor = 5;
            var augendData = new double[] { 10, 20, 30, 40, 50, 60 };
            var augend = new DenseSymmetricMatrix<double>(augendData);
            var addendData = new double[] { 1, 2, 3 };
            var addend = new DenseSymmetricMatrix<double>(addendData);
            var augendRowsIndexes = new int[2];
            augendRowsIndexes[position] = augendRowIndex;

            var augendColumnsIndexes = new[] { 0, 1 };

            // Act and Assert
            var parameterName = string.Format(CultureInfo.InvariantCulture, "augendRowsIndexes[{0}]", position);
            Assert.That(
                () => augend.Add(augendRowsIndexes, augendColumnsIndexes, addend, Factor),
                Throws.TypeOf<ElementIndexIsOutOfRangeException>().With.Property("ParamName").EqualTo(parameterName));
        }

        /// <summary>
        ///     Tests the method when an augend column index is negative or greater than or equal to size of augend matrix.
        /// </summary>
        /// <param name="position">
        ///     The position of index.
        /// </param>
        /// <param name="augendColumnIndex">
        ///     The column index of augend element.
        /// </param>
        [Test]
        public void WhenAnAugendColumnIndexIsOutOfRange([Values(0, 1)] int position, [Values(-1, 3)] int augendColumnIndex)
        {
            // Arrange
            const double Factor = 5;
            var augendData = new double[] { 10, 20, 30, 40, 50, 60 };
            var augend = new DenseSymmetricMatrix<double>(augendData);
            var addendData = new double[] { 1, 2, 3 };
            var addend = new DenseSymmetricMatrix<double>(addendData);
            var augendRowsIndexes = new[] { 0, 2 };
            var augendColumnsIndexes = new int[2];
            augendColumnsIndexes[position] = augendColumnIndex;

            // Act and Assert
            var parameterName = string.Format(CultureInfo.InvariantCulture, "augendColumnsIndexes[{0}]", position);
            Assert.That(
                () => augend.Add(augendRowsIndexes, augendColumnsIndexes, addend, Factor),
                Throws.TypeOf<ElementIndexIsOutOfRangeException>().With.Property("ParamName").EqualTo(parameterName));
        }

#endif
    }
}